import javax.swing.*;
import javax.swing.border.EmptyBorder;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class SvoiKozyri extends JFrame
{  

   private static final long serialVersionUID = 1L;
   private final int SMALL_WIDTH = 500;
   private final int SMALL_HEIGHT = 300;
   private final int FULL_WIDTH = 1000;
   private final int FULL_HEIGHT = 660;
   
   private final int CARD_PADDING = 15;
   private final int CARD_LIFT = 15;
   
   final int MAX_VERT_CARDS = 5;
   final int MAX_HORIZ_CARDS = 9;
   
   private final int DIAMONDS = 1;
   private final int HEARTS = 2;
   private final int CLUBS = 3;
   private final int SPADES = 4;
   
   private Game _game;
   
   private Container _contentPane;
   
   private SplashScreen _splash;
   private ChooseSuitScreen _chooseSuit;
   private DrawCardScreen _drawCards;
   private Board _board;
   private AllCards _allCards;
   private JPanel _pilePane;
   private JPanel _endScreen;
   
   private int _trumpsChosen;
   
   private ArrayList<String> _toPlay;
   private Timer _timer = new Timer(1000, new NextTurn());
   
   public SvoiKozyri()
   {
      _game = new Game();
      _contentPane = getContentPane();
      _contentPane.setLayout(new CardLayout());
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setTitle("Svoi Kozyri");
      setResizable(false);
      setIconImage(null);
      
      _splash = new SplashScreen();
      _chooseSuit = new ChooseSuitScreen();
      _drawCards = new DrawCardScreen();
      
      _pilePane = new JPanel();
      _pilePane.setOpaque(false);
      
      _contentPane.add(_splash, "Splash");
      _contentPane.add(_chooseSuit, "Choose Suit");
      _contentPane.add(_drawCards, "Draw");
      
      _toPlay = new ArrayList<String>();
      
      showSmall("Splash");
      setVisible(true);
   }
   
   
   /**
    * Changes the view to be the given smaller panel
    * 
    * @param panelToShow The name of the panel to switch to
    */
   public void showSmall(String panelToShow)
   {
      setSize(SMALL_WIDTH, SMALL_HEIGHT);
      setLocationRelativeTo(null); // center
      
      ((CardLayout)_contentPane.getLayout()).show(_contentPane, panelToShow);
   }
   
   
   /**
    * Changes the view to be the board
    */
   public void showBoard()
   {    
      setSize(FULL_WIDTH, FULL_HEIGHT);
      setLocationRelativeTo(null); // center
      
      ((CardLayout)_contentPane.getLayout()).show(_contentPane, "Board");
   }
   
   
   /**
    * Paints/repaints the card pile
    */
   void paintCardPile()
   {  
      Card top;
      JPanel pile = new JPanel();
      JPanel actions = new JPanel(new GridLayout(2, 1, 5, 5));
      JButton play = new JButton("Play Cards");
      JButton draw = new JButton("Draw");
      
      pile.setOpaque(false);
      actions.setOpaque(false);
      
      play.addActionListener(new PlayCards());
      draw.addActionListener(new ViewDraw());
      
      // remove current image if we're repainting
      _pilePane.removeAll();
      
      try
      {
         top = _game._deck.getTop();
         
         // paint new card
         pile.add(new JLabel(new ImageIcon("img/cards/" + Integer.toString(top.getDenomination()) + Integer.toString(top.getSuit()) + ".png")));
      }
      catch (IndexOutOfBoundsException e)
      {
         pile.add(new JLabel(new ImageIcon("img/cards/emptyPile.png")));
      }
      
      actions.add(play);
      actions.add(draw);
      
      _pilePane.add(pile);
      _pilePane.add(actions);
   }


   /** 
    * Paints the end screen which shows the order of winners
    */
   void paintEndScreen()
   {
      _endScreen = new ImagePanel(new ImageIcon("img/bgcropped.jpg").getImage());
      _endScreen.setLayout(new GridBagLayout());
      GridBagConstraints constraints = new GridBagConstraints();
      
      JLabel gameDone = new JLabel(new ImageIcon("img/finished.png"));
      JLabel winOrder;
      StringBuilder b = new StringBuilder();
      int i = 1;
      
      b.append("<html><font color='white' size='5'>");
      
      // get order of winners
      for(Player player : _game._winOrder)
      {
         b.append(i + ". <b>Player " + player.getPlayerNum() + "</b><br />");
         i++;
      }
      
      b.append("</font></html>");
      
      winOrder = new JLabel(b.toString());
      
      constraints.gridy = 0;
      _endScreen.add(gameDone, constraints);
      constraints.insets = new Insets(10, 0, 0, 0);
      constraints.gridy = 1;
      _endScreen.add(winOrder, constraints);
      
      _contentPane.add(_endScreen, "End");
      showSmall("End");
   }
   
   
   /**
    * Helper method that creates an image button with no border, no margin,
    * no JButton background. 
    * 
    * @param name The name of the button
    * @param fileName The image that will represent the button
    * @param listener The action listener for the button
    * @return The completed button
    */
   private JButton makeButton(String name, String fileName, ActionListener listener)
   {
      JButton button = new JButton(new ImageIcon(fileName));
      button.setOpaque(false);
      button.setContentAreaFilled(false);
      button.setBorderPainted(false);
      button.setFocusPainted(false);
      button.setMargin(new Insets(0,0,0,0));
      button.setName(name);
      button.addActionListener(listener);
      button.setBorder(new EmptyBorder(0, 0, 0, 0));
      
      return button;
   }
   
   
   /**
    * An action listener for when the number of players is being selected
    * Sets up the game with the given number of players and changes the
    * screen to be the player trump choice screen
    */
   private class NumPlayersListener implements ActionListener
   {  
      public void actionPerformed(ActionEvent event)
      {
         // get the name of the clicked button (number of players)
         String source = ((JButton) event.getSource()).getName();
         
         // get the number of human players from the name of the clicked button
         _game.setupPlayers(Integer.parseInt(source));
         
         // go to the next screen
         showSmall("Choose Suit");
      }
   }
   
   
   /**
    * An action listener that listens for the trump choice of each human player
    * Assigns the selected trump to the player, and once all human players
    * have chosen, the computer trumps are set, game is dealt, and view changes
    * to the board view
    */
   private class PlayerTrumpListener implements ActionListener
   {  
      public void actionPerformed(ActionEvent event)
      {
         // get the name of the clicked button (number of players)
         JButton source = (JButton) event.getSource();
         
         // get the number of human players from the name of the clicked button
         _game.getCurrentPlayer().setTrump(Integer.parseInt(source.getName()));
         _game.getNextPlayer();
         
         // keep track of how many trumps we've chosen
         _trumpsChosen += 1;
         // remove that option from the pane when we next repaint
         _chooseSuit.nextChoice(source);
         
         // if we've gotten all of the trumps from the human players, then
         // set the computer trumps if we have any computer players
         if(!_game.isPlayerHuman() || _trumpsChosen == 4)
         {
            if(!_game.isPlayerHuman())
            {
               _game.setComputerTrumps();
            }
            
            // start the game, make the board, and show the board
            _game.startGame();
            _board = new Board();
            _allCards = new AllCards();
            _contentPane.add(_board, "Board");
            _contentPane.add(_allCards, "All Cards");
            showBoard();
         }
      }
   }
   
   
   /**
    * An action listener that will show the draw cards panel when clicked, or will
    * alert the user that there are no cards to draw if so.
    */
   private class ViewDraw implements ActionListener
   {  
      public void actionPerformed(ActionEvent event)
      {
         // only do this if it's the player's turn
         if(_game.isPlayerHuman())
         {
            // alert the user that there are no cards to draw if the play pile is empty
            if(_game._deck.isEmpty())
            {
               JOptionPane.showMessageDialog(null, "The play pile is currently empty");
            }
            // show the draw panel
            else
            {
               _drawCards.paintPanel();
               ((CardLayout)_contentPane.getLayout()).show(_contentPane, "Draw");
            }
         }
         else
         {
            JOptionPane.showMessageDialog(null, "It is not your turn!");
         }
      }
   }
   
   
   /**
    * An action listener that will perform the drawing of cards for a player
    * and then move on to the next player
    */
   private class DrawCards implements ActionListener
   {  
      public void actionPerformed(ActionEvent event)
      {          
         _game.drawCards();
         nextTurn();
      }
   }
   
   
  /**
   * An action listener that adds a card to the set of cards the player wants
   * to play. Doesn't allow for more than 2 cards. Changes the position of the card
   * by bumping it up in the pane to show that it's been selected
   */
  private class AddPlayCard implements ActionListener
   {  
      public void actionPerformed(ActionEvent event)
      {
         JButton source = (JButton) event.getSource();

         if(_game.isPlayerHuman())
         {
            // if the user has already selected to cards, don't allow them to select more
            if(_toPlay.size() == 2)
            {
               JOptionPane.showMessageDialog(null, "You have already selected two cards");
            }
            else
            {
               // add the card to our set
               _toPlay.add(source.getName());
               
               // remove this action listener from the card
               for(ActionListener listener : source.getActionListeners()) 
               {
                  source.removeActionListener(listener);
               }
               
               // bump the card up above the other cards to show that it's been selected
               source.setBorder(new EmptyBorder(0, 0, CARD_LIFT, 0));
               
               // add an action listener to remove the cards from the ones to play
               source.addActionListener(new RemovePlayCard());
            }
         }
         // if the user tries to play when it's not their turn, don't do anything and just yell at them instead
         else
         {
            JOptionPane.showMessageDialog(null, "It's not your turn!");
         }
      }   
   }
  
  
  /**
   * An action listener that removes a selected card from the set of cards the player wants
   * to play. Changes the position of the card by bumping it back down to be in line with
   * the other cards.
   */
  private class RemovePlayCard implements ActionListener
  {  
     public void actionPerformed(ActionEvent event)
     {
        JButton source = (JButton) event.getSource();
        
        if(_game.isPlayerHuman())
        {
           // remove this card from our list of cards to play
           _toPlay.remove(source.getName());
           
           // remove this listener
           for(ActionListener listener : source.getActionListeners()) 
           {
              source.removeActionListener(listener);
           }
           
           // bump the card back down to be in line with the other cards
           source.setBorder(new EmptyBorder(CARD_PADDING, 0, 0, 0));
           
           // add an action listener that lets this card be selected again
           source.addActionListener(new AddPlayCard());
        }
        // if the user tries to play when it's not their turn, don't do anything and just yell at them instead
        else
        {
           JOptionPane.showMessageDialog(null, "It's not your turn!");
        }
     }   
  }
  
  
  /**
   * An action listener that will play the set of cards the user has selected
   * Will then repaint the board for the next turn. Continues to repaint the
   * board as long as it's a computer player (with a short delay inbetween 
   * animations so the user can see what has been played).
   */
  private class PlayCards implements ActionListener
  {  
     
     public void actionPerformed(ActionEvent event)
     {
        Card[] cards;
        Card singleCard;
        String[] parts;
        boolean returnValue = true;
        
        // if the player is human
        if(_game.isPlayerHuman())
        {
           // if they haven't selected any cards then alert them and do nothing else
           if(_toPlay.size() < 1)
           {
              JOptionPane.showMessageDialog(null, "Please select cards to play");
           }
           // otherwise, if this is the first card we're playing or the player's
           // hand only has one card in it, then play this card
           else if(_toPlay.size() == 1)
           {
              if(!_game._deck.isEmpty() && _game.getCurrentPlayer().getSize() != 1)
              {
                 JOptionPane.showMessageDialog(null, "You must select two cards to play");
              }
              else
              {
                 parts = _toPlay.get(0).split("_");
                 singleCard = new Card(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
                 
                 returnValue = _game.playSingleCard(singleCard);
              }
           }
           // otherwise, if we have two cards to play and the deck isn't empty, play the cards
           else if(_toPlay.size() == 2 && !_game._deck.isEmpty())
           {
              cards = new Card[_toPlay.size()];
              
              // play the cards
              for(int i = 0; i < _toPlay.size(); i++)
              {
                 parts = _toPlay.get(i).split("_");
                 cards[i] = new Card(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
              }
              
              returnValue = _game.playCards(cards);
           }
           // if the player has selected more than one card, but this is the first play, tell them
           // to only select one. Reset the cards to play 
           else if(_toPlay.size() == 2 && _game._deck.isEmpty())
           {
              JOptionPane.showMessageDialog(null, "Please only select one card to play when the deck is empty");
              _toPlay.clear();
              _board.updateBoard();
           }
           
           // if the play was invalid, alert the user and reset the cards to play
           if(!returnValue)
           {
              JOptionPane.showMessageDialog(null, "Your card does not beat the top card. Select again.");
              _toPlay.clear();
              _board.updateBoard();
           }
           // if it worked, then move on to the next player
           else
           {
              nextTurn();
           }
        }
        // if the user tries to play when it's not their turn, don't do anything and just yell at them instead
        else
        {
           JOptionPane.showMessageDialog(null, "It's not your turn!");
        }
     }   
  }
  
  
  /**
   * Method that is called after a player makes a move and gets the board
   * ready for the next player. If the next player is a computer, then 
   * the computer player will take their move and the board will continue
   * to animate and make the computer player move until the next human player.
   * If the next player doesn't have any playable cards and the deck is empty,
   * then they will be notified that they have to pass.
   */
  private void nextTurn()
  {
     // clear current cards and redraw the board
     _toPlay.clear();
     _allCards.removeAll();
     _board.updateBoard();
     showBoard();
     

     // if the next player isn't human and it's not the end of the game
     // then start the animation that goes through the computer player moves
     if(!_game.isPlayerHuman() && !_game.isGameOver())
     {
        // play computer move
        _game.playComputerRound();
        
        // start the animation
        _timer.start(); 
     }
     else if(_game.isPlayerHuman() && !_game.isGameOver())
     {
        // otherwise, at the start of this next turn, if the player only has one
        // card and it's not playable, and they can't draw, then let them know they 
        // have to pass and go on to the next player
        if(_game.getCurrentPlayer().getSize() == 1 && 
           !_game.beatsTop(_game.getCurrentPlayer()._hand.get(0)) &&
           _game._deck.isEmpty())
        {
           JOptionPane.showMessageDialog(null, "You have no valid cards to play, you must pass");
           nextTurn();
        }
     }
     else
     {
        // game is over, show end screen
        paintEndScreen();
     }
  }
  
  
  /**
   * An action method that is called from the nextTurn() method's animator
   * that will go through all of the computer player turns, pausing for a second
   * before each turn so that the player can see the state of the board. Stops the
   * animation when the next player is a human player.
   */
  private class NextTurn implements ActionListener
  {  
     public void actionPerformed(ActionEvent event)
     {
        // clear the toPlay set and reset the board view
        _toPlay.clear();
        _allCards.removeAll();
        _board.updateBoard();
        showBoard();
        
        // If the current player is a computer and the game isn't over
        // make an automated move. Otherwise, stop the animation and 
        // wait for player input
        if(!_game.isPlayerHuman() && !_game.isGameOver())
        {
           _game.playComputerRound();
        }
        else
        {
           _timer.stop();
           
           // if the game is over, then show the win screen
           if(_game.isGameOver())
           {
              paintEndScreen();
           }
        }
     }
     
  }
   
   
  /**
   * ImagePanel class that is the welcome/player selection screen
   */
   private class SplashScreen extends ImagePanel
   {
      private static final long serialVersionUID = 1L;

      SplashScreen()
      {
         super(new ImageIcon("img/bgcropped.jpg").getImage());
         
         // get panels ready
         JPanel buttons = new JPanel();
         JLabel welcome = new JLabel(new ImageIcon("img/welcomeBanner.png"));
         JLabel instructions = new JLabel("<html><font color='white'>Please choose a player configuration:</font></html>");
         JButton oneHuman = makeButton("1", "img/oneHuman.png", new NumPlayersListener());
         JButton twoHuman = makeButton("2", "img/twoHuman.png", new NumPlayersListener());
         JButton threeHuman = makeButton("3", "img/threeHuman.png", new NumPlayersListener());
         JButton fourHuman = makeButton("4", "img/fourHuman.png", new NumPlayersListener());

         // clear backgrounds
         buttons.setOpaque(false);
   
         // add buttons to the button panel
         buttons.add(oneHuman);
         buttons.add(twoHuman);
         buttons.add(threeHuman);
         buttons.add(fourHuman);
         
         // add all elements
         add(welcome);
         add(instructions);
         add(buttons);
      }
   }
   
   
   /**
    * ImagePanel class that is the screen where human players choose
    * their trumps.
    */
   private class ChooseSuitScreen extends ImagePanel
   {
      private static final long serialVersionUID = 1L;
      private JLabel _instructions;
      private JPanel _buttons;
      
      ChooseSuitScreen()
      {
         super(new ImageIcon("img/bgcropped.jpg").getImage());
         
         // set up some panel things
         GridBagConstraints constraints = new GridBagConstraints();
         setLayout(new GridBagLayout());
         constraints.fill = GridBagConstraints.NONE;
         
         // set up the buttons and labels
         _buttons = new JPanel();
         JLabel title = new JLabel(new ImageIcon("img/trumpChoice.png"));
         _instructions = new JLabel("<html><center><font color='white'><font size='25'>Player 1</font><br>Please choose a trump suit:</font></center><br></html>");
         JButton diamonds = makeButton("1", "img/diamonds.png", new PlayerTrumpListener());
         JButton hearts = makeButton("2", "img/hearts.png", new PlayerTrumpListener());
         JButton clubs = makeButton("3", "img/clubs.png", new PlayerTrumpListener());
         JButton spades = makeButton("4", "img/spades.png", new PlayerTrumpListener());
         
         // get rid of panel backgrounds
         _buttons.setOpaque(false);

         // add the buttons to the button panel
         _buttons.add(diamonds);
         _buttons.add(hearts);
         _buttons.add(clubs);
         _buttons.add(spades);
         
         constraints.gridx = 0;
         
         // add the following vertically
         constraints.gridy = 0;
         add(title, constraints);
         
         constraints.gridy = 1;
         add(_instructions, constraints);
         
         constraints.gridy = 2;
         add(_buttons, constraints);
         
         // no trumps have been assigned yet
         _trumpsChosen = 0;
      }
      
      // this method is called after a trump is chosen and will remove the choice from the 
      // list of trumps for the next player to choose from, and updates the instrutions 
      // line that says which player should choose their trump next
      void nextChoice(JButton source)
      {
         int playerNum = _trumpsChosen + 1;
         
         _instructions.setText("<html><center><font color='white'><font size='25'>Player " + playerNum + "</font><br>Please choose a trump suit:</font></center><br></html>");
         
         _buttons.remove(source);
         revalidate();
         repaint();
      }
   }
   
   
   /**
    * An ImagePanel class that displays all of the cards that the user would pick up
    * if they decided to draw
    */
   private class DrawCardScreen extends ImagePanel
   {
      private static final long serialVersionUID = 1L;
      
      JPanel _cards;
      
      /**
       * constructor to initialize a couple of things
       */
      DrawCardScreen()
      {
         super(new ImageIcon("img/bgcropped.jpg").getImage());
         setLayout(new GridBagLayout());
      }
      
      
      /**
       * Paints/repaints the panel to show the cards that the user would pick
       * up if they decided to draw them. Gives options to return to the board
       * or to confirm drawing the cards
       */
      void paintPanel()
      {
         ArrayList<Card> cards = _game._deck.peek(_game.getCurrentPlayer().getTrump());
         int suit;
         int denom;
         int numCardsInRow = cards.size();
         String cardFileName;
         JButton returnToBoard = new JButton("Return to Board");
         JButton confirmDraw = new JButton("Confirm Draw");
         GridBagConstraints constraints = new GridBagConstraints();
         
         // set up buttons
         returnToBoard.addActionListener(new ShowBoard());
         confirmDraw.addActionListener(new DrawCards());
         
         // clear the panel in case we're repainting
         removeAll();
         
         // set the width of the grid
         if(cards.size() > MAX_HORIZ_CARDS)
         {
            numCardsInRow = MAX_HORIZ_CARDS;
         }
         
         _cards = new JPanel(new GridLayout(0, numCardsInRow, 5, 5));
         _cards.setOpaque(false);
         
         // for every card that would be drawn, add them to the panel
         for(Card card : cards)
         {
            suit = card.getSuit();
            denom = card.getDenomination();
            cardFileName = "img/cards/" + Integer.toString(denom) + Integer.toString(suit) + ".png";
            
            _cards.add(new JLabel(new ImageIcon(cardFileName)));
         }
         
         // add all elements
         constraints.insets = new Insets(50, 50, 0, 50);
         constraints.gridy = 0;
         add(_cards, constraints);
         constraints.insets = new Insets(10, 0, 0, 0);
         constraints.gridy = 1;
         add(returnToBoard, constraints);
         constraints.gridy = 2;
         add(confirmDraw, constraints);
      }
      
      
      /**
       * An action listener for the return to board button that takes the user
       * back to the board view
       */
      private class ShowBoard implements ActionListener
      {  
         public void actionPerformed(ActionEvent event)
         {          
            showBoard();
         }
      }
   }
   
   
   /**
    * Internal class for creating Svoi Kozyi game. Creates the panel 
    * for the board.
    */
   private class Board extends ImagePanel
   {
      private static final long serialVersionUID = 1L;
      
      private JPanel _top;
      private JPanel _left;
      private JPanel _center;
      private JPanel _middle;
      private JPanel _right;
      private JPanel _bottom;
      private JPanel _northTrump;
      private JPanel _southTrump;
      private JPanel _eastTrump;
      private JPanel _westTrump;
      
      Board()
      {              
         super((new ImageIcon("img/bgcropped.jpg").getImage()));
         
         // local variables
         Player[] players = _game.getPlayersAsArray();
         
         // set board layout and put some padding on the edges
         setLayout(new BorderLayout());
         setBorder(new EmptyBorder(10, 10, 10, 10));

         // initialize all panels and set any layouts
         _top = new JPanel();
         _top.setPreferredSize(new Dimension(FULL_WIDTH, 140));
         _middle = new JPanel(new BorderLayout());
            _left = new JPanel(new GridBagLayout());
            _left.setPreferredSize(new Dimension(130, 600));
            _center = new JPanel(new BorderLayout());
               _northTrump = new JPanel(new GridBagLayout());
               _southTrump = new JPanel(new GridBagLayout());
               _eastTrump = new JPanel(new GridBagLayout());
               _westTrump = new JPanel(new GridBagLayout());
           _right = new JPanel(new GridBagLayout());
           _right.setPreferredSize(new Dimension(130, 600));
         _bottom = new JPanel();
         _bottom.setPreferredSize(new Dimension(FULL_WIDTH, 140));
         
         // set all to be opaque
         _top.setOpaque(false);
         _middle.setOpaque(false);
            _left.setOpaque(false);
            _center.setOpaque(false);
               _northTrump.setOpaque(false);
               _southTrump.setOpaque(false);
               _eastTrump.setOpaque(false);
               _westTrump.setOpaque(false);
            _right.setOpaque(false);
         _bottom.setOpaque(false);
         
         // paint trump icons and number of cards
         paintPlayerInfo(_northTrump, players[2]);
         paintPlayerInfo(_southTrump, players[0]);
         paintPlayerInfo(_eastTrump, players[3]);
         paintPlayerInfo(_westTrump, players[1]);
         
         // paint cards per player
         paintCurrentPlayerCards(players[0].getHand());
         paintCardSet(_left, players[1].getHand(), true);
         paintCardSet(_top, players[2].getHand(), false);
         paintCardSet(_right, players[3].getHand(), true);
         
         // finally, paint the board
         paintPanel();
      }
      
      
      /**
       * Paints or repaints the cards for the current player on the
       * bottom section of the board.
       * 
       * @param hand the list of cards that the current player has
       */
      void paintCurrentPlayerCards(ArrayList<Card> hand)
      {
         int numCards = 0;
         int suit;
         int denom;
         String cardFileName;
         JButton viewAllCards = new JButton("View All Cards");
         JButton card;
               
         // if this is a repaint, we need to clear it
         _bottom.removeAll();
         
         // for every card in the hand, get it's suit and denomination and use that
         // to determine the image file for the card. Then add it to the panel.
         while(numCards < MAX_HORIZ_CARDS && numCards < hand.size())
         {
            suit = hand.get(numCards).getSuit();
            denom = hand.get(numCards).getDenomination();
            cardFileName = "img/cards/" + Integer.toString(denom) + Integer.toString(suit) + ".png";
            card = makeButton(Integer.toString(denom) + "_" + Integer.toString(suit), cardFileName, new AddPlayCard());
            card.setBorder(new EmptyBorder(CARD_PADDING, 0, 0, 0));
            
            _bottom.add(card);
            numCards++;
         }
         
         // if there more cards in the players hand than we can display, give them the option
         // to view all of their cards in another pane
         if(numCards < hand.size())
         {
            viewAllCards.addActionListener(new AllCardsListener());
            _bottom.add(viewAllCards);
         }
      
      }
      
      
      /**
       * Paints or repaints the card backs for the given hand and panel.
       * If there are more than 6 cards in the left or right panel hands, only
       * six cards will be painted. Same goes for the top panel, but the max
       * is 9 cards.
       * 
       * @param panel The top, left, or right panel to paint
       * @param hand The cards to paint
       * @param rotated Boolean indicating if the cards should be rotated
       */
      void paintCardSet(JPanel panel, ArrayList<Card> hand, boolean rotated)
      {
         String cardBack = "img/cards/back.png";
         int numCardsToDisplay = hand.size();
         GridBagConstraints constraints = new GridBagConstraints();
         int start = 0;
         
         // if this is a repaint, we need to clear it
         panel.removeAll();
         
         // if it's the left or right sides, change the image to the
         // rotated version and divide the number to diplay by two
         // since our images show two cards per image
         if(rotated)
         {
            numCardsToDisplay /= 2;
            numCardsToDisplay += 2;
            
            cardBack = "img/cards/backRotated.png";
            
            if(hand.size() == 1)
            {
               cardBack = "img/cards/backRotatedOne.png";
            }
         }
         
         // determine number of cards to display
         // if there are more than 6 cards in the right or left
         // panel hands, then only display 6
         if(numCardsToDisplay > MAX_VERT_CARDS && rotated)
         {
            numCardsToDisplay = MAX_VERT_CARDS;
         }
         // if there are more than 9 cards in the hand for the
         // top panel, only paint 9
         else if(numCardsToDisplay > MAX_HORIZ_CARDS && !rotated)
         {
            numCardsToDisplay = MAX_HORIZ_CARDS;
         }
         
         if(rotated)
         {
            constraints.gridy = 0;
            panel.add(new JLabel(new ImageIcon("img/cards/backRotatedFirst.png")), constraints);
            numCardsToDisplay--;
            start++;
         }
         
         // add the card images to the panel
         for(int i = start; i < numCardsToDisplay; i++)
         {
            constraints.gridy = i;
            
            panel.add(new JLabel(new ImageIcon(cardBack)), constraints);
         }
         
         // finish off our rotated set
         if(rotated)
         {
            constraints.gridy = numCardsToDisplay + 2;
            panel.add(new JLabel(new ImageIcon("img/cards/backRotatedLast.png")), constraints);
         }
      }
      
      
      /**
       * Paints or repaints the panel that displays the player's trump and
       * how many cards they have
       * 
       * @param panel The panel to paint the information to
       * @param player The player that we're displaying the information of
       */
      void paintPlayerInfo(JPanel panel, Player player)
      {
         GridBagConstraints constraints = new GridBagConstraints();
         String cardsWord = "Cards";
         
         // make "cards" grammatically correct if there's only one
         if(player.getSize() == 1)
         {
            cardsWord = "Card";
         }
         
         // if this is a repaint, we need to clear it
         panel.removeAll();
         
         // add the trump icon
         panel.add(paintTrumpIcon(player), constraints);
         
         // display the number of cards below the trump icon
         constraints.gridy = 1;
         panel.add(new JLabel("<html><font color='white'>" + Integer.toString(player.getHand().size()) + " " + cardsWord + "</font></html>"), constraints);
      }
      
      
      /**
       * Gets the trump icon for a given player
       * 
       * @param player The player to get the trump of
       * @return A JLabel that has a trump icon in it for the given player
       */
      JLabel paintTrumpIcon(Player player)
      {
         String iconFileName = "";
         
         // select icon depending on what the trump is
         if(player.getTrump() == DIAMONDS)
         {
            iconFileName = "diamondIcon";
         }
         else if(player.getTrump() == HEARTS)
         {
            iconFileName = "heartIcon";
         }
         else if(player.getTrump() == CLUBS)
         {
            iconFileName = "clubIcon";
         }
         else if(player.getTrump() == SPADES)
         {
            iconFileName = "spadeIcon";
         }
         
         // if the current player is this player, then the icon
         // needs to be highlighted
         if(_game.getCurrentPlayer().equals(player))
         {
            iconFileName += "Highlighted";
         }
         
         return new JLabel(new ImageIcon("img/" + iconFileName + ".png"));
         
      }

      
      /**
       * Paints/repaints the board when switching between the 
       * allCards panel and the board because the pilePane panel
       * had to be traded between the two
       */
      void paintPanel()
      {
         removeAll();
         
         // paint pile
         _pilePane.setBorder(new EmptyBorder(10, 0, 0, 0));
         paintCardPile();
         
         // add player info panels to center panel of middle panel
         _center.add(_northTrump, BorderLayout.NORTH);
         _center.add(_southTrump, BorderLayout.SOUTH);
         _center.add(_eastTrump, BorderLayout.EAST);
         _center.add(_westTrump, BorderLayout.WEST);
         _center.add(_pilePane, BorderLayout.CENTER);
         
         // add left, center, and right sections to middle panel of board
         _middle.add(_left, BorderLayout.WEST);
         _middle.add(_center, BorderLayout.CENTER);
         _middle.add(_right, BorderLayout.EAST);
         
         // add top and bottom sections to board
         add(_top, BorderLayout.PAGE_START);
         add(_middle, BorderLayout.CENTER);
         add(_bottom, BorderLayout.PAGE_END);
      }
      
      
      /**
       * Repaints the board for the next turn
       */
      void updateBoard()
      {
         Player[] players = new Player[4];
         
         // if this is a human player, then we need to repaint
         // the locations of the cards so that the new human
         // player can see their cards
         if(_game.isPlayerHuman())
         {
            players = _game.getPlayersAsArray();
         }
         // otherwise, we want to keep getting them in the designated order
         // where player 1 is always on the bottom
         else
         {
            players[0] = _game.getPlayer(1);
            players[1] = _game.getPlayer(2);
            players[2] = _game.getPlayer(3);
            players[3] = _game.getPlayer(4);
         }
         
         // repaint pile
         paintCardPile();
         
         // repaint trump icons
         paintPlayerInfo(_northTrump, players[2]);
         paintPlayerInfo(_southTrump, players[0]);
         paintPlayerInfo(_eastTrump, players[3]);
         paintPlayerInfo(_westTrump, players[1]);
         
         // repaint card sets
         paintCurrentPlayerCards(players[0].getHand());
         paintCardSet(_left, players[1].getHand(), true);
         paintCardSet(_top, players[2].getHand(), false);
         paintCardSet(_right, players[3].getHand(), true);
         
         revalidate();
         repaint();
      }

      
      /**
       * An action listener for when the user has more cards than can be displayed.
       * When clicked, it will switch the layout to show all of the user's cards
       */
      private class AllCardsListener implements ActionListener
      {  
         public void actionPerformed(ActionEvent event)
         {  
            // repaint the panel so that the pilePane shows on it
            _allCards.paintPanel();
            // show the panel
            ((CardLayout)_contentPane.getLayout()).show(_contentPane, "All Cards");
         }
      }
   }
   
   
   /**
    * ImagePanel class that displays all of the current player's cards. This is
    * for when the player has more cards than can be displayed on the board. Also
    * displays the pile and game actions.
    */
   class AllCards extends ImagePanel
   {
      private static final long serialVersionUID = 1L;
      
      private JPanel _cards;
      private JButton _returnToBoard;
      
      /**
       * constructor to set up basics
       */
      AllCards()
      {
         super((new ImageIcon("img/bgcropped.jpg").getImage()));
         
         setLayout(new GridBagLayout());
         
         // get the return button ready
         _returnToBoard = new JButton("Return to Board");
         _returnToBoard.addActionListener(new ShowBoard());
         
         // set some margins and layouts for the cards
         _cards = new JPanel(new GridLayout(0, 11, 5, 5));
         _cards.setOpaque(false);
      }
      
      
      /**
       * Paint/repaint the panel with the current player's cards and the
       * play pile. Because the board also uses the play pile panel, we
       * have to trade it back and forth between the two
       */
      void paintPanel()
      {
         GridBagConstraints constraints = new GridBagConstraints();
         
         // get the play pile ready
         _pilePane.setBorder(new EmptyBorder(10, 0, 0, 0));
         paintCards();
         
         // add all elements
         constraints.insets = new Insets(15, 15, 15, 15);
         constraints.gridy = 0;
         add(_pilePane, constraints);
         constraints.gridy = 1;
         add(_cards, constraints);
         constraints.gridy = 2;
         add(_returnToBoard, constraints);
      }
      
      
      /**
       * Paint the cards
       */
      void paintCards()
      {
         ArrayList<Card> hand = _game.getCurrentPlayer().getHand();
         int suit;
         int denom;
         String cardFileName;
         JButton cardButton;   
         
         // get rid of any preexisting cards in the panel
         _cards.removeAll();
         
         // put all cards from the player's hand on the panel
         for(Card card : hand)
         {
            suit = card.getSuit();
            denom = card.getDenomination();
            cardFileName = "img/cards/" + Integer.toString(denom) + Integer.toString(suit) + ".png";
            cardButton = makeButton(Integer.toString(denom)  + "_" +  Integer.toString(suit), cardFileName, new AddPlayCard());
            cardButton.setBorder(new EmptyBorder(CARD_PADDING, 0, 0, 0));
            
            _cards.add(cardButton);
         } 
      }
      
      
      /**
       * An action listener for the return to board button that takes the user
       * back to the board view
       */
      private class ShowBoard implements ActionListener
      {  
         public void actionPerformed(ActionEvent event)
         {          
            // clear the all cards panel so that the board can have the play
            // pile back, then repaint the board, then show the board
            _allCards.removeAll();
            _board.paintPanel();
            showBoard();
         }
      }
   }
   
   
   /**
    * Main method that starts the game 
    * 
    * @param args
    */
   public static void main(String[] args)
   {
      SvoiKozyri gui = new SvoiKozyri();
   }
}
